/******************************************************************************
*
* This file is part of FreeTrack (http://code.google.com/p/qtfreetrack).
* BY Willy Gardiol (willy@gardiol.org)
*
* FreeTrack is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FreeTrack is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************/
#include "trackgps.h"

#include "trackpoint.h"
#include "layertrack.h"
#include "configwidget.h"
#include "layermanager.h"
#include "ftpushbutton.h"
#include "ftlabel.h"
#include "fthelpwidget.h"

extern ConfigWidget* conf;
extern LayerManager* layerManager;
TrackGps* gps;

TrackGps::TrackGps(QWidget *parent) :
    FreeTrackWidget(parent),
    current_position(),
    gpx_has_fix(false) // GPS is always not in fix at startup
{
    // For access from other places
    gps = this;

#ifdef GPS_DEBUG
    QFile pts( "e:\\debugPoints.txt");
    if (pts.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&pts);
        while ( !in.atEnd() )
        {
            TrackPoint temp_tp;
            QString row = in.readLine();
            QString what = row.left(3);
            temp_tp.fromTxt( row.mid(3) );
            gpsPoints.append( temp_tp );
        }
    }
    gps_debug_timer = new QTimer();
#endif

    // Initialize current position to the last position, if stored.
    if ( conf->getSavePosition() )
        current_position = TrackPoint( conf->getInitialPosition() );

    position_label = new FTLabel( tr("Current position (dec/WGS84)"));
    position_label->setAlignment(Qt::AlignCenter);
    position = new FTLabel( "--.--Lat --.--Lon" );
    position->setAlignment(Qt::AlignCenter);
    altitude_label = new FTLabel( tr("Current altitude"));
    altitude_label->setAlignment(Qt::AlignCenter);
    altitude = new FTLabel( "--" );
    altitude->setAlignment(Qt::AlignCenter);
    precision_label = new FTLabel( tr("Precision"));
    precision_label->setAlignment(Qt::AlignCenter);
    precision = new FTLabel( "--" );
    precision->setAlignment(Qt::AlignCenter);
    speed_label = new FTLabel( tr("Current speed"));
    speed_label->setAlignment(Qt::AlignCenter);
    speed = new FTLabel( "--" );
    speed->setAlignment(Qt::AlignCenter);

    gpsHelp = new FTHelpWidget(
       tr("<center|b>GPS fix info</center>"\
          "<justify>Here you can check all the data reported by the GPS device.</justify>"\
          "<center|b>Position</center>"\
          "<justify>The current position is reported in latitude and longitude. The format "\
          "is based on the WGS84 datum, and its reported in decimal degrees and deciman fractions. "\
          "Currently, FreeTrack does not support any other output format.</justify>"\
          "<center|b>Altitude and Precision</center>"\
          "<justify>FreeTrack report the current altitude as indicated by the GPS device "\
          "and its expressed in meter. Right now it is not possible to have it reported in feet, "\
          "if you require it please open a new issue on FreeTrack home page (see the about dialog). "\
          "If requires, it is possible to specify an 'altitude correction' factor which will be added "\
          "to the altitude. This correction factor can be specified from the FreeTrack configuration page. "\
          "FreeTrack also report the current horizontal precision, expressed in meters.</justify>"\
          "<center|b>Speed</center>"\
          "<justify>FreeTrack will also report the speed, in kilometers per hour. At this point it is not "\
          "possible to change this unit. Please open an issue if you need kph or another format. "\
          "Since a few GPS devices do not report speed correctly, FreeTrack will integrate it and "\
          "calculate an average speed, which will be less accurate, when the speed is not available "\
          "from the GPS device. Calculated speeds will be shows with a '(calc)' notation on the screen. "\
          "This feature is optional and can be activated or deactivated from the configuration page.</justify>") );

    speed_label_calc = false;


    // Connect some pushbuttons...
    gps_timer = new QTimer(this);
    gps_timer->setSingleShot(true);
    // Create the GPS object...
    m_location = QGeoPositionInfoSource::createDefaultSource(this);
    if ( m_location != NULL )
    {             
        // we want an update as soon as it is available (we will filter ourselves)
        m_location->setUpdateInterval( m_location->minimumUpdateInterval()/2.0 );

#ifndef GPS_DEBUG
        connect(m_location, SIGNAL(positionUpdated(QGeoPositionInfo)),
                      this, SLOT(updateFromGps(QGeoPositionInfo)));
#else
        connect( gps_debug_timer, SIGNAL(timeout()),
                      this, SLOT(debugUpdateFromGps()));
        gps_debug_timer->start(10000);
#endif
        connect(m_location, SIGNAL(updateTimeout()),
                      this, SLOT(gpsTimeout()));
        connect( gps_timer, SIGNAL(timeout()),
                      this, SLOT(gpsTimeout()) );
        // Immediately start updating the GPS (try to fix position)
        m_location->startUpdates();
        gps_timer->start(  m_location->minimumUpdateInterval()*2.0 );
    }

    connect(      this, SIGNAL(gpsUpdate(const TrackPoint&)),
                  // Store livetrack for later use
             layerManager->getLivetrack(), SLOT(addPoint(const TrackPoint&)) );
}

TrackGps::~TrackGps()
{
    if ( m_location != NULL )
    {
        m_location->stopUpdates();
        delete m_location;
    }
    delete gps_timer;
}



QPointF TrackGps::currentPosition()
{
    return current_position;
}

void TrackGps::gpsTimeout()
{
    gpx_has_fix = false;
    position->setText( "--.--Lat --.--Lon" );
    altitude->setText( "--" );
    precision->setText( "--" );
    speed->setText("--");
    emit gpsStatusChange( gpx_has_fix );
}

#ifdef GPS_DEBUG
void TrackGps::debugUpdateFromGps()
{
    if ( gpsPoints.size() > 0 )
    {
        QGeoPositionInfo qgp;
        QGeoCoordinate coord;
        TrackPoint dpt = gpsPoints.takeFirst();
        QPointF pt = dpt;
        coord.setLatitude(pt.y());
        coord.setLongitude(pt.x());
        coord.setAltitude( 300.0);
        qgp.setCoordinate( coord );
        qgp.setTimestamp( dpt.getTimestamp() );
        qgp.setAttribute( QGeoPositionInfo::HorizontalAccuracy, dpt.getPrecision() );
        qgp.setAttribute( QGeoPositionInfo::GroundSpeed, dpt.getSpeed() );
        updateFromGps( qgp );
    }
}
#endif

void TrackGps::updateFromGps(QGeoPositionInfo gpsPos)
{
    // Did we received a valid position update?
    if ( gpsPos.isValid() )
    {   // Yes. Do we have already a valid fix?
        if ( !gpx_has_fix )
        {   // No we dont, so lets announce we now have a valid fix
            gpx_has_fix = true;
            emit gpsStatusChange( gpx_has_fix );
        }
        // Reset gps_timer
        gps_timer->start(  m_location->minimumUpdateInterval()*5.0 );
        // Coordinate comes from gpsPos
        QGeoCoordinate coord = gpsPos.coordinate();
        coord.setAltitude( coord.altitude() + conf->getAltitudeCorrection() );
        // Timestamp comes from gpsPos too
        QDateTime time = gpsPos.timestamp();
        // Precision also is retuerned from gpsPos (not always, but mostly)
        qreal newPrecision = 1.0;
        if ( gpsPos.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) )
            newPrecision = gpsPos.attribute(QGeoPositionInfo::HorizontalAccuracy);
        // Speed is not always returned... mostly here we will calculate it.
        qreal newSpeed = 0.0;
        if ( gpsPos.hasAttribute(QGeoPositionInfo::GroundSpeed) )
        {
            if ( speed_label_calc )
            {
                speed_label->setText( tr("Current speed") );
                speed_label_calc = false;
            }
            newSpeed = gpsPos.attribute(QGeoPositionInfo::GroundSpeed);
        }
        else if ( conf->getCalcSpeed() ) // but only if enabled!
        {   // We can calculate speed... (note: until first fix, this can be "wrong"!)
            if ( !speed_label_calc )
            {
                speed_label->setText( tr("Current speed (calc)") );
                speed_label_calc = true;
            }
            qreal delta_t = time.toTime_t() - current_position.getTimestamp().toTime_t();
            qreal distance = current_position.distanceFrom( coord );
            if ( distance > 0.0 )
            {
                if ( delta_t >= 1.0 )
                    newSpeed = distance / delta_t;
            }
        }
        else
            newSpeed = 0.0;
        // Create the point...
        current_position = TrackPoint( coord, time, newPrecision, newSpeed );
        // Update the screen...
        position->setText( QString("%1Lat %2Lon").arg(coord.latitude(), 0, 'f', 5)
                                           .arg(coord.longitude(), 0, 'f', 5) );
        altitude->setText( QString("%1m").arg(coord.altitude(),0,'f',0) );
        precision->setText( QString("%1m").arg(newPrecision,0,'f',0) );
        speed->setText( QString("%1km/h").arg(newSpeed*3.6,0,'f',0) );
        conf->setInitialPosition( current_position );
        emit gpsUpdate( current_position );
    }
    // No valid position update means we lost GPS fix...
    else if ( gpx_has_fix )
        gpsTimeout();
}

void TrackGps::postInitialize()
{
   emit gpsUpdate( current_position );
}


QLayout* TrackGps::createHorizontal()
{
    QHBoxLayout* grid = new QHBoxLayout();
    grid->addWidget( gpsHelp );

    QHBoxLayout* pos_grid = new QHBoxLayout();
    pos_grid->addWidget( position_label );
    pos_grid->addWidget( position );
    QHBoxLayout* alt_grid = new QHBoxLayout();
    alt_grid->addWidget( altitude_label );
    alt_grid->addWidget( altitude );
    QHBoxLayout* pre_grid = new QHBoxLayout();
    pre_grid->addWidget( precision_label );
    pre_grid->addWidget( precision );
    QHBoxLayout* spd_grid = new QHBoxLayout();
    spd_grid->addWidget( speed_label );
    spd_grid->addWidget( speed );

    QVBoxLayout* data_grid = new QVBoxLayout();
    data_grid->addLayout( pos_grid );
    data_grid->addLayout( alt_grid );
    data_grid->addLayout( pre_grid );
    data_grid->addLayout( spd_grid );
    grid->addLayout( data_grid );
    return grid;
}

QLayout* TrackGps::createVertical()
{
    QVBoxLayout* grid = new QVBoxLayout();
    QHBoxLayout* hgrid = new QHBoxLayout();
    hgrid->addWidget( gpsHelp );
    hgrid->addStretch();
    grid->addLayout( hgrid );

    grid->addWidget( position_label );
    grid->addWidget( position );
    grid->addWidget( altitude_label );
    grid->addWidget( altitude );
    grid->addWidget( precision_label );
    grid->addWidget( precision );
    grid->addWidget( speed_label );
    grid->addWidget(speed );
    return grid;
}
